package repos

import java.sql.Timestamp

import models._
import org.joda.time.DateTime
import slick.lifted.ProvenShape

import scala.reflect._

object Tables {

  import slick.driver.MySQLDriver.api._


  implicit def jdateColumnType =
    MappedColumnType.base[DateTime, Timestamp](
      dt => new Timestamp(dt.getMillis),
      ts => new DateTime(ts.getTime)
    )

  implicit def seqStringColumnType = MappedColumnType.base[Seq[String], String](
    ss => ss.mkString(","),
    s => s.split(",").toSeq
  )

  abstract class BaseTable[T <: BaseModel : ClassTag](tag: Tag, tableName: String) extends Table[T](tag, tableName) {
    val classOfEntity = classTag[T].runtimeClass

    val id = column[Int]("id", O.PrimaryKey, O.Length(45), O.AutoInc)
    val isDeleted = column[Boolean]("isDeleted", O.Default(false), O.SqlType("varchar(1)"))
    val joinTime = column[DateTime]("joinTime", O.SqlType("datetime"))
    val updateTime = column[Option[DateTime]]("updateTime", O.SqlType("datetime"))
  }

  class UserTable(tag: Tag) extends BaseTable[User](tag, "user") {

    val userName = column[String]("userName", O.Length(45))
    val password = column[String]("password", O.Length(45))
    val email = column[String]("email", O.Length(45))
    val userStatus = column[String]("userStatus", O.Length(2))
    val mobilePhoneNo = column[Option[String]]("mobilePhoneNo", O.Length(20))
    val nickName = column[Option[String]]("nickName", O.Length(25))

    val country = column[Option[String]]("country", O.Length(10))
    val postCode = column[Option[String]]("postCode", O.Length(10))
    val city = column[Option[String]]("city", O.Length(10))

    val uniUserName = index("uniUserUserName", userName, true)
    val uniEmail = index("uniUserEmail", email, true)
    val uniMobilePhoneNo = index("uniUserMobilePhone", mobilePhoneNo, true)

    def * = (userName,
      password,
      email,
      userStatus,
      joinTime.?,
      mobilePhoneNo,
      nickName,
      id.?,
      updateTime,
      isDeleted,
      country,
      postCode,
      city
    ) <> (User.tupled, User.unapply)
  }

  class OrganizationTable(tag: Tag) extends BaseTable[Organization](tag, "organization") {
    val orgName = column[String]("organizationName", O.Length(45))
    val orgPath = column[String]("organizationAddress", O.SqlType("text"))
    val orgAddress = column[Option[String]]("organizationAddress", O.Length(200))
    val parentId = column[Option[String]]("parentId", O.Length(45))

    //    val parent = foreignKey("fkOrganizationParentId",parentId,organizations)(_.id.?)

    def * : ProvenShape[Organization] = (
      orgName,
      orgPath,
      isDeleted,
      joinTime.?,
      id.?,
      orgAddress,
      parentId,
      updateTime
    ) <> (Organization.tupled, Organization.unapply)
  }

  class DictItemTable(tag: Tag) extends BaseTable[DictItem](tag, "dictItem") {
    val dictCode = column[String]("dictCode", O.Length(45))
    val value = column[String]("value", O.Length(45))
    val display = column[String]("display", O.Length(45))
    val orderNum = column[Int]("orderNum", O.Length(4))
    val dictId = column[Int]("dictId", O.Length(45))
    val dict = foreignKey("fkDictItemDictId", dictId, dicts)(_.id)

    override def * : ProvenShape[DictItem] = (
      value,
      display,
      orderNum,
      joinTime.?,
      isDeleted,
      dictId,
      updateTime,
      id.?
    ) <> (DictItem.tupled, DictItem.unapply)
  }

  class DictTable(tag: Tag) extends BaseTable[Dict](tag, "dict") {
    val dictCode = column[String]("dictCode", O.Length(45))
    val dictName = column[String]("dictName", O.Length(45))
    val dictType = column[String]("dictType", O.Length(2))
    val dictDescription = column[Option[String]]("dictDescription", O.Length(200))

    def lDictItems = dicts join dictItems on (_.id === _.dictId)

    override def * = (
      dictCode,
      dictName,
      dictType,
      joinTime.?,
      isDeleted,
      dictDescription,
      id.?,
      updateTime
    ) <> (Dict.tupled, Dict.unapply)
  }

  class RoleTable(tag: Tag) extends BaseTable[Role](tag, "role") {
    val roleCode = column[String]("roleCode",O.Length(45))
    val roleName = column[String]("roleName",O.Length(45))
    val roleDescrition = column[Option[String]]("roleDescrition")

    override def * : ProvenShape[Role] = (
      roleCode,
      roleName,
      joinTime.?,
      isDeleted,
      roleDescrition,
      updateTime,
      id.?
    ) <> (Role.tupled, Role.unapply)
  }

  class UserRoleTable(tag: Tag) extends BaseTable[UserRole](tag, "userRole") {
    val roleId = column[Int]("roleId", O.Length(45))
    val userId = column[Int]("userId", O.Length(45))

    val role = foreignKey("fkUserRoleRoleId", roleId, roles)(_.id)
    val user = foreignKey("fkUserRoleUserId", userId, users)(_.id)

    override def * : ProvenShape[UserRole] = (
      userId,
      roleId,
      joinTime.?,
      isDeleted,
      updateTime,
      id.?
    ) <> (UserRole.tupled, UserRole.unapply)
  }

  class RoleModuleTable(tag: Tag) extends BaseTable[RoleModule](tag, "roleModule") {
    val roleId = column[Int]("roleId", O.Length(45))
    val moduleId = column[Int]("moduleId", O.Length(45))

    val role = foreignKey("fkRoleModuleRoleId", roleId, roles)(_.id)
    val module = foreignKey("fkRoleModuleModuleId", moduleId, modules)(_.id)

    override def * : ProvenShape[RoleModule] = (
      roleId,
      moduleId,
      joinTime.?,
      isDeleted,
      updateTime,
      id.?
    ) <> (RoleModule.tupled, RoleModule.unapply)
  }

  class ModuleTable(tag: Tag) extends BaseTable[Module](tag, "module") {

    val moduleName = column[String]("moduleName",O.Length(45))
    val moduleClass = column[String]("moduleClass",O.Length(100))
    val moduleOrder = column[Int]("moduleOrder",O.Length(3))
    val moduleRequireModuleClasses = column[Option[Seq[String]]]("moduleRequireModuleClasses",O.Length(500))
    val moduleOptionalParams = column[Option[Seq[String]]]("moduleOptionalParams",O.Length(500))
    val moduleIconClass = column[Option[String]]("moduleIconClass",O.Length(50))
    val moduleParentId = column[Option[Int]]("moduleParentId", O.Length(45))
    //    val parentFk = foreignKey("fkModuleParentId", moduleParentId, modules)(_.id.?)

    def parent = modules join modules on (_.moduleParentId === _.id) map (_._2)

    override def * = (
      moduleName,
      moduleClass,
      moduleOrder,
      joinTime.?,
      isDeleted,
      moduleRequireModuleClasses,
      moduleOptionalParams,
      moduleIconClass,
      moduleParentId,
      updateTime,
      id.?
    ) <> (Module.tupled, Module.unapply)
  }

  class JurisdictionTable(tag: Tag) extends BaseTable[Jurisdiction](tag, "jurisdiction") {
    val juriCode = column[String]("juriCode",O.Length(12))
    val juriName = column[String]("juriName",O.Length(50))
    val juriPath = column[String]("juriPath",O.Length(500))
    val juriParentId = column[Option[Int]]("juriParentId", O.Length(45))

    override def * : ProvenShape[Jurisdiction] = (
      juriCode,
      juriName,
      juriPath,
      juriParentId,
      joinTime.?,
      isDeleted,
      id.?,
      updateTime
    ) <> (Jurisdiction.tupled, Jurisdiction.unapply)
  }


  val jurisdictions = TableQuery[JurisdictionTable]
  val users = TableQuery[UserTable]
  val roles = TableQuery[RoleTable]
  val userRoles = TableQuery[UserRoleTable]
  val roleModules = TableQuery[RoleModuleTable]
  val organizations = TableQuery[OrganizationTable]
  val dicts = TableQuery[DictTable]
  val dictItems = TableQuery[DictItemTable]
  val modules = TableQuery[ModuleTable]

}
